package com.zhlq.condition;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import com.zhlq.exception.ReflectException;
import com.zhlq.exception.WhereException;
import com.zhlq.util.ClassUtil;
import com.zhlq.util.StringUtil;

/**
 * 生成实例“数据模型”的查询SQL或HQL条件。 实体的属性严格遵循以<b><font color='red'>小写开头</font></b>。
 * 使用时，T的实例Entity的getter方法和settter方法一定要规范，
 * 作为条件添加的属性的getter和setter对应的字段一定要在数据库中存在。<br/>
 * 为了较好的性能，这里只做一般性检查，不做严格检查
 */
@SuppressWarnings("rawtypes")
public class Where {

	// 泛型的具体类的所有公共getter的名称，即所有公共可访问字段
	private List<String> fields = new ArrayList<String>();
	// 当前实例的Class
	private Class entityClass;

	// 未左右闭合的括号数量
	private int unsealedBracketCount = 0;

	/** 连接符'AND' */
	private static final String JOINER_AND = " AND ";
	/** 连接符'OR' */
	private static final String JOINER_OR = " OR ";

	/** 当前的条件连接符AND或者OR，默认为AND */
	private String joiner = JOINER_AND;
	/** 是否检查字段 */
	private boolean checkField = true;
	/** 是否忽略空值或空字符串，默认为true */
	private boolean ignoreNullAndEmpty = true;

	/** 用于拼装的查询字符串 */
	private StringBuilder ql = new StringBuilder(" ");
	/** 查询字符串中的参数，它用于代替问号(?)表示的占位符 */
	private List<Object> params = new ArrayList<Object>();

	// @TODO 注意或许无需使用泛型
	/**
	 * 通过实例的Class构造，默认检查字段，进行HQL查询时推荐使用
	 * 
	 * @param clz
	 */
	public Where(Class clz) {
		entityClass = clz;
		// 取得所有getter和setter，当两者都存在时则认为这是一个合法字段
		// 但是考虑到效率还是让使用者自己规范，这里就不做过多的检查了。
		Method[] mds = clz.getMethods();
		for (Method md : mds) {
			// 取得符合"getter和setter"的方法名
			String mdName = md.getName();
			if (mdName.startsWith("get")) {
				fields.add(mdName.substring(3, 4).toLowerCase()
						+ mdName.substring(4));
			}
		}
	}

	/**
	 * 取得条件部分的查询语句
	 * 
	 * @return
	 */
	public String queryString() {
		if (unsealedBracketCount != 0) {
			throw new WhereException("存在" + unsealedBracketCount + "个未封闭的括号");
		}
		return ql.toString().trim();
	}

	/**
	 * 取得参数的List
	 * 
	 * @return
	 */
	public List<Object> getParamList() {
		return params;
	}

	/**
	 * 取得参数的数组
	 * 
	 * @return
	 */
	public Object[] getParams() {
		return params.toArray();
	}

	/**
	 * 检查属性字段是否存在于当前实例类型中
	 * 
	 * @param field
	 */
	private void check(String field) {
		if (checkField && fields.contains(field) == false) {
			throw new WhereException("field is not exists. 当前实例类型"
					+ entityClass.getName() + "中不存在字段[" + field + "]");
		}
	}

	/**
	 * 判断是否忽略空值或空字符串，true不忽略，false忽略。
	 * 
	 * @return
	 */
	private boolean notIgnore(Object value) {
		if (ignoreNullAndEmpty) {
			return value != null && value.toString().length() > 0;
		} else {
			return true;
		}
	}

	/**
	 * 设定工作模式
	 * 
	 * @param mode
	 */
	public void setMode(int mode) {
		switch (mode) {
		case MODE_JOINER_AND:
			joiner = JOINER_AND;
			break;
		case MODE_JOINER_OR:
			joiner = JOINER_OR;
			break;
		case MODE_CHECK:
			checkField = true;
			break;
		case MODE_NOT_CHECK:
			checkField = false;
			break;
		case MODE_IGNORE:
			ignoreNullAndEmpty = true;
			break;
		case MODE_NOT_IGNORE:
			ignoreNullAndEmpty = false;
			break;
		default:
			break;
		}
	}

	/**
	 * 确认当前是否为HQL查询，true为HQL查询，false为SQL查询。<BR/>
	 * HQL查询会检查字段是否存在；SQL则不会检查，也不无法检查。
	 * 
	 * @return
	 */
	public boolean isHQL() {
		return checkField;
	}

	/**
	 * 括号开始
	 */
	public void startBracket() {
		ql.append(joiner + " (");
		unsealedBracketCount++;
	}

	/**
	 * 括号结束
	 */
	public void endBracket() {
		ql.append(") ");
		unsealedBracketCount--;
	}

	/**
	 * 追加连接符
	 */
	public void appendJoiner() {
		if (ql.charAt(ql.length() - 1) != '(') {
			ql.append(joiner);
		}
	}

	/**
	 * 添加“等于”
	 * 
	 * @param field
	 * @param value
	 */
	public void addEqual(String field, Object value) {
		check(field);
		if (notIgnore(value)) {
			appendJoiner();
			ql.append(field + "=?");
			params.add(value);
		}
	}

	/**
	 * 添加“不等于”
	 * 
	 * @param field
	 * @param value
	 */
	public void addNotEqual(String field, Object value) {
		check(field);
		if (notIgnore(value)) {
			appendJoiner();
			ql.append(field + "!=?");
			params.add(value);
		}
	}

	/**
	 * 添加“大于”
	 * 
	 * @param field
	 * @param value
	 */
	public void addGreater(String field, Object value) {
		check(field);
		if (notIgnore(value)) {
			appendJoiner();
			ql.append(field + ">?");
			params.add(value);
		}
	}

	/**
	 * 添加“大于等于”
	 * 
	 * @param field
	 * @param value
	 */
	public void addGreaterEq(String field, Object value) {
		check(field);
		if (notIgnore(value)) {
			appendJoiner();
			ql.append(field + ">=?");
			params.add(value);
		}
	}

	/**
	 * 添加“小于”
	 * 
	 * @param field
	 * @param value
	 */
	public void addLess(String field, Object value) {
		check(field);
		if (notIgnore(value)) {
			appendJoiner();
			ql.append(field + "<?");
			params.add(value);
		}
	}

	/**
	 * 添加“小于等于”
	 * 
	 * @param field
	 * @param value
	 */
	public void addLessEq(String field, Object value) {
		check(field);
		if (notIgnore(value)) {
			appendJoiner();
			ql.append(field + "<=?");
			params.add(value);
		}
	}

	/**
	 * 添加“模糊匹配like”的最常用型[%value%]
	 * 
	 * @param field
	 * @param value
	 */
	public void addLike(String field, String value) {
		check(field);
		if (notIgnore(value)) {
			appendJoiner();
			ql.append(field + " LIKE ?");
			params.add("%" + value + "%");
		}
	}

	/**
	 * 添加“模糊匹配like”，使用自定义的pattern匹配，value仅用于空值校验
	 * 
	 * @param field
	 * @param value
	 * @param pattern
	 */
	public void addLike(String field, String value, String pattern) {
		check(field);
		if (notIgnore(value)) {
			appendJoiner();
			ql.append(field + " LIKE ?");
			params.add(pattern);
		}
	}

	/**
	 * 添加“区间类型条件”
	 * 
	 * @param field
	 * @param startEq是否包含起始值
	 * @param start
	 * @param endEq是否包含结束值
	 * @param end
	 */
	public void addBetween(String field, boolean startEq, Object start,
			boolean endEq, Object end) {
		check(field);
		if (notIgnore(start)) {
			if (startEq) {
				appendJoiner();
				ql.append(field + ">=?");
				params.add(start);
			} else {
				appendJoiner();
				ql.append(field + ">?");
				params.add(start);
			}
		}
		if (notIgnore(end)) {
			if (endEq) {
				appendJoiner();
				ql.append(field + "<=?");
				params.add(end);
			} else {
				appendJoiner();
				ql.append(field + "<?");
				params.add(end);
			}
		}
	}

	/**
	 * 添加“区间类型条件，大于等于 ‘且’ 小于等于，即 field>=? AND field<=?”
	 * 
	 * @param field
	 * @param start
	 * @param end
	 */
	public void addBetween(String field, Object start, Object end) {
		addBetween(field, true, start, true, end);
	}

	/**
	 * 添加“IN查询条件”
	 * 
	 * @param field
	 * @param items
	 */
	public void addIn(String field, Object... objs) {
		check(field);
		List<Object> items = Arrays.asList(objs);
		if (items == null || items.isEmpty()) {
			return;
		}
		// 为了支持remove操作
		items = new ArrayList<Object>(items);
		// 空值忽略
		Iterator<Object> it = items.iterator();
		while (it.hasNext()) {
			Object item = it.next();
			if (notIgnore(item) == false) {
				it.remove();
			}
		}

		if (!items.isEmpty()) {
			appendJoiner();
			ql.append(field + " IN (");
			for (Object item : items) {
				ql.append("?,");
				params.add(item);
			}
			ql.deleteCharAt(ql.length() - 1);
			ql.append(")");
		}
	}
	
	public void addIns(String field, Object[] objs) {
		check(field);
		List<Object> items = Arrays.asList(objs);
		if (items == null || items.isEmpty()) {
			return;
		}
		// 为了支持remove操作
		items = new ArrayList<Object>(items);
		// 空值忽略
		Iterator<Object> it = items.iterator();
		while (it.hasNext()) {
			Object item = it.next();
			if (notIgnore(item) == false) {
				it.remove();
			}
		}

		if (!items.isEmpty()) {
			appendJoiner();
			ql.append(field + " IN (");
			for (Object item : items) {
				ql.append("?,");
				params.add(item);
			}
			ql.deleteCharAt(ql.length() - 1);
			ql.append(")");
		}
	}

	/**
	 * 添加“NOT IN查询条件”
	 * 
	 * @param field
	 * @param items
	 */
	public void addNotIn(String field, Object... objs) {
		check(field);
		List<Object> items = Arrays.asList(objs);
		if (items == null || items.isEmpty()) {
			return;
		}
		// 为了支持remove操作
		items = new ArrayList<Object>(items);
		// 空值忽略
		Iterator<Object> it = items.iterator();
		while (it.hasNext()) {
			Object item = it.next();
			if (notIgnore(item) == false) {
				it.remove();
			}
		}

		if (!items.isEmpty()) {
			appendJoiner();
			ql.append(field + " NOT IN (");
			for (Object item : items) {
				ql.append("?,");
				params.add(item);
			}
			ql.deleteCharAt(ql.length() - 1);
			ql.append(")");
		}
	}

	/** 模式:查询字符串连接符为AND */
	public static final int MODE_JOINER_AND = 1;
	/** 模式:查询字符串连接符为OR */
	public static final int MODE_JOINER_OR = 2;
	/** 模式:检查字段，并转换为HQL查询 */
	public static final int MODE_CHECK = 3;
	/** 模式:不检查字段，并转换为SQL查询 */
	public static final int MODE_NOT_CHECK = 4;
	/** 模式:忽略NULL值或空字符串 */
	public static final int MODE_IGNORE = 5;
	/** 模式:不忽略NULL值或空字符串 */
	public static final int MODE_NOT_IGNORE = 6;

	public Class getEntityClass() {
		return entityClass;
	}

	public void setEntityClass(Class entityClass) {
		this.entityClass = entityClass;
	}

	public String getEntityName() {
		return (null != entityClass) ? entityClass.getName() : "";
	}

	public void add(String condition) {
		ql.append(condition);
	}

	public void add(String field, Object value) {
		appendJoiner();
		ql.append(field+"=?");
		this.params.add(value);
	}

	public void add(String field, String compare, Object value) {
		appendJoiner();
		ql.append(field+compare+"?");
		this.params.add(value);
	}

	public void add(Condition condition) {
		if (null == condition) {
			return;
		}
		// 默认比较符为“=”
		if (null == condition.getCompare() || "".equals(condition.getCompare().trim())) {
			condition.setCompare("=");
		}
		if(null == condition.getKey() || "".equals(condition.getKey().trim())){
			// 属性名为空
			return;
		} else if(null == condition.getValue()){
			// 值为null
		} else if("=".equals(condition.getCompare().trim())){
			if((StringUtil.isEmpty(condition.getValue()) && !ignoreNullAndEmpty) || StringUtil.isNotEmptyNull(condition.getValue()) || ClassUtil.isBaseClass(condition.getValue())) {
				// value为空字符串"",且没有忽略空字符串/value为基础类型或者
				appendJoiner();
				this.add(condition.getKey()+"=?");
				this.params.add(condition.getValue());
			}
		} else if("!=".equals(condition.getCompare().trim())){
			if((StringUtil.isEmpty(condition.getValue()) && !ignoreNullAndEmpty) || StringUtil.isNotEmptyNull(condition.getValue()) || ClassUtil.isBaseClass(condition.getValue())) {
				// value为空字符串"",且没有忽略空字符串/value为基础类型
				appendJoiner();
				this.add(condition.getKey()+"!=?");
				this.params.add(condition.getValue());
			}
		} else if(">".equals(condition.getCompare().trim())){
			if((StringUtil.isEmpty(condition.getValue()) && !ignoreNullAndEmpty) || StringUtil.isNotEmptyNull(condition.getValue()) || ClassUtil.isBaseClass(condition.getValue())) {
				// value为空字符串"",且没有忽略空字符串/value为基础类型
				appendJoiner();
				this.add(condition.getKey()+">?");
				this.params.add(condition.getValue());
			}
		} else if(">=".equals(condition.getCompare().trim())){
			if((StringUtil.isEmpty(condition.getValue()) && !ignoreNullAndEmpty) || StringUtil.isNotEmptyNull(condition.getValue()) || ClassUtil.isBaseClass(condition.getValue())) {
				// value为空字符串"",且没有忽略空字符串/value为基础类型
				appendJoiner();
				this.add(condition.getKey()+">=?");
				this.params.add(condition.getValue());
			}
		} else if("<".equals(condition.getCompare().trim())){
			if((StringUtil.isEmpty(condition.getValue()) && !ignoreNullAndEmpty) || StringUtil.isNotEmptyNull(condition.getValue()) || ClassUtil.isBaseClass(condition.getValue())) {
				// value为空字符串"",且没有忽略空字符串/value为基础类型
				appendJoiner();
				this.add(condition.getKey()+"<?");
				this.params.add(condition.getValue());
			}
		} else if("<=".equals(condition.getCompare().trim())){
			if((StringUtil.isEmpty(condition.getValue()) && !ignoreNullAndEmpty) || StringUtil.isNotEmptyNull(condition.getValue()) || ClassUtil.isBaseClass(condition.getValue())) {
				// value为空字符串"",且没有忽略空字符串/value为基础类型
				appendJoiner();
				this.add(condition.getKey()+"<=?");
				this.params.add(condition.getValue());
			}
		} else if("in".equals(condition.getCompare().trim())){
			List<Object> objects = null;
			if(null == condition.getValue()){
				// ""
			} else if(StringUtil.isEmpty(condition.getValue())){
				// null
			} else if(StringUtil.isNotEmpty(condition.getValue())){
				// "abc"
				objects = new ArrayList<Object>();
				String[] tmps = ((String)condition.getValue()).split(",");
				for(String s : tmps){
					objects.add(s);
				}
			} else if (ClassUtil.isArrayBaseOrString(condition.getValue())){
				objects = new ArrayList<Object>();
				Object[] tmps = (Object[]) condition.getValue();
				for(Object o : tmps){
					objects.add(o);
				}
			} else if (ClassUtil.isCollectionBaseOrString(condition.getValue())){
				objects = new ArrayList<Object>();
				Object[] tmps = (Object[]) condition.getValue();
				for(Object o : tmps){
					objects.add(o);
				}
			}
			if(!ClassUtil.isSameListBaseOrString(objects)){
				return ;
			}
			appendJoiner();
			String sql = condition.getKey()+" in (";
			for(Object o : objects){
				if(!ignoreNullAndEmpty && StringUtil.isNotEmptyNull(o)){
					sql+="?,";
					this.params.add(o);
				}
			}
			if(sql.endsWith(",")){
				sql=sql.substring(0,sql.length()-1);
				sql+=")";
				this.add(sql);
			}
		} else if("not in".equals(condition.getCompare().trim())){
			List<Object> objects = null;
			if(null == condition.getValue()){
				// ""
			} else if(StringUtil.isEmpty(condition.getValue())){
				// null
			} else if(StringUtil.isNotEmpty(condition.getValue())){
				// "abc"
				objects = new ArrayList<Object>();
				String[] tmps = ((String)condition.getValue()).split(",");
				for(String s : tmps){
					objects.add(s);
				}
			} else if (ClassUtil.isArrayBaseOrString(condition.getValue())){
				objects = new ArrayList<Object>();
				Object[] tmps = (Object[]) condition.getValue();
				for(Object o : tmps){
					objects.add(o);
				}
			} else if (ClassUtil.isCollectionBaseOrString(condition.getValue())){
				objects = new ArrayList<Object>();
				Object[] tmps = (Object[]) condition.getValue();
				for(Object o : tmps){
					objects.add(o);
				}
			}
			if(!ClassUtil.isSameListBaseOrString(objects)){
				return ;
			}
			appendJoiner();
			String sql = condition.getKey()+" not in (";
			for(Object o : objects){
				if(!ignoreNullAndEmpty && StringUtil.isNotEmptyNull(o)){
					sql+="?,";
					this.params.add(o);
				}
			}
			if(sql.endsWith(",")){
				sql=sql.substring(0,sql.length()-1);
				sql+=")";
				this.add(sql);
			}
		} else if("like".equals(condition.getCompare().trim())){
			if((StringUtil.isEmpty(condition.getValue()) && !ignoreNullAndEmpty) || StringUtil.isNotEmptyNull(condition.getValue())) {
				// value为空字符串"",且没有忽略空字符串/value为基础类型
				appendJoiner();
				this.add(condition.getKey()+" like ?");
				this.params.add("%"+condition.getValue()+"%");
			}
		} else if("not like".equals(condition.getCompare().trim())){
			if((StringUtil.isEmpty(condition.getValue()) && !ignoreNullAndEmpty) || StringUtil.isNotEmptyNull(condition.getValue())) {
				// value为空字符串"",且没有忽略空字符串/value为基础类型
				appendJoiner();
				this.add(condition.getKey()+" not like ?");
				this.params.add("%"+condition.getValue()+"%");
			}
		} else if("left like".equals(condition.getCompare().trim())){
			if((StringUtil.isEmpty(condition.getValue()) && !ignoreNullAndEmpty) || StringUtil.isNotEmptyNull(condition.getValue())) {
				// value为空字符串"",且没有忽略空字符串/value为基础类型
				appendJoiner();
				this.add(condition.getKey()+" like ?");
				this.params.add(condition.getValue()+"%");
			}
		} else if("right like".equals(condition.getCompare().trim())){
			if((StringUtil.isEmpty(condition.getValue()) && !ignoreNullAndEmpty) || StringUtil.isNotEmptyNull(condition.getValue())) {
				// value为空字符串"",且没有忽略空字符串/value为基础类型
				appendJoiner();
				this.add(condition.getKey()+" like ?");
				this.params.add("%"+condition.getValue());
			}
		} else if("is null".equals(condition.getCompare().trim())){
			appendJoiner();
			this.add(condition.getKey()+" is null");
		} else if("is not null".equals(condition.getCompare().trim())){
			appendJoiner();
			this.add(condition.getKey()+" is not null");
		} else if("empty".equals(condition.getCompare().trim())){
			appendJoiner();
			this.add(condition.getKey()+"=''");
		} else if("not empty".equals(condition.getCompare().trim())){
			appendJoiner();
			this.add(condition.getKey()+"!=''");
		} else if("null or empty".equals(condition.getCompare().trim())){
			appendJoiner();
			this.add(" ("+condition.getKey()+" is null or "+condition.getKey()+"='')");
		} else if("not (null or empty)".equals(condition.getCompare().trim())){
			appendJoiner();
			this.add(" !("+condition.getKey()+" is null or "+condition.getKey()+"='')");
		} else {
			// 其他情况
		}
	}
	
	public static Where beanToWhere(Object object) {
		Where where = new Where(object.getClass());
		Field[] fields = object.getClass().getDeclaredFields();
		for(Field f : fields){
			if("class".equals(f.getName()) || "serialVersionUID".equals(f.getName())){
				continue;
			}
			try {
				Method method = object.getClass().getDeclaredMethod("get"+f.getName().substring(0,1).toUpperCase()+f.getName().substring(1));
				Object value = method.invoke(object);
				if(null != value){					
					where.add(f.getName(), value);
				}
			} catch(NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
				// 反射异常：调用方法
				(new ReflectException("调用方法", e)).doing();
			}
		}
		return where;
	}

	public void orderBy(String order) {
		ql.append(" ORDER BY " + order);
	}

}
